﻿#region - Class details -
/*  
 *  Developer   : Wicus Botha
 *  Description : 
 *  History     : WB - 2009-12-0 - Created
 */
#endregion

using System;
using System.Data;
using System.Linq;
using System.Text;
using IQ.DbBase;

namespace SqlServerAdmin
{
    public class SqlMetaData
    {
        public delegate void Progress(int position);
        public event Progress SetMaxPosition;
        public event Progress SetCurrentPosition;
        //
        public enum FieldChangeType : byte
        {
            NoChange = 0,
            // Change types
            NewField = 10,
            Deleted = 11,
            OrderChange = 13,
            // length,prec,scale,colstat,isnullable,FldType,DefaultVal
            DefValue = 20,
            Nullable = 21,
            DataType = 22,
            Length = 23,
            Precision = 24,
            Scale = 25,
            ColStat = 26
        }
        public enum ChangeType : byte
        {
            NoChange = 0,
            Changed = 5,
            NotExistInFile = 8,
            NotExistOnServer = 9,
            New = 10,
            Deleted = 11,
            IndexStatus = 30
            //NotInDestination = 20,
            //NotInSource = 21
        }
        #region # Constructor #
        public SqlMetaData()
        {
            createChanges();
            loadServerData();
            currentDb = DbHelper.Database;
            exclusions = SqlMdFileData.Instance.GetExcludeList(currentDb);
        }
        #endregion

        #region - Private variables -
        private string currentDb;
        #endregion

        #region - Private methods -
        private string getChangeString(ChangeType change)
        {
            switch (change)
            {
                case ChangeType.Changed : return "Changed";
                case ChangeType.Deleted : return "Deleted";
                case ChangeType.New : return "New";
                case ChangeType.NotExistInFile: return "Not exists in file";
                case ChangeType.NotExistOnServer: return "Not exists on server";
                case ChangeType.IndexStatus: return "Index status changed";
                default: return "";
            }
        }
        #endregion

        #region - Protected variables -
        protected DataTable dtChanges;
        protected DataTable dtTemp;
        protected DataTable tmpFields;
        protected string[] exclusions;
        #endregion

        #region - Protected methods -
        protected delegate bool compareItem(string nameType, string name);
        protected virtual void loadServerData()
        { 
        }
        protected void setMaxPosition(int value)
        {
            if (SetMaxPosition != null)
                SetMaxPosition(value);
        }
        protected void setCurrentPosition(int value)
        {
            if (SetCurrentPosition != null)
                SetCurrentPosition(value);
        }
        protected virtual void createChanges()
        {
            dtChanges = new DataTable();
            DbHelper.AddDataColumn(dtChanges, "Name", "System.String", false, "");
            DbHelper.AddDataColumn(dtChanges, "ChangeType", "System.Byte", true, 0);
            DbHelper.AddDataColumn(dtChanges, "Change", "System.String", true, "");
            DbHelper.AddKeyToTable(dtChanges, "Name");
        }
        protected void addToChanges(string name, ChangeType change)
        {
            DataRow dr = dtChanges.Select("Name='" + name + "'").FirstOrDefault();
            if (dr != null)
            {
                dr["ChangeType"] = (byte)change;
                dr["Change"] = getChangeString(change);
            }
            else
            {
                dr = dtChanges.NewRow();
                dr["Name"] = name;
                dr["ChangeType"] = (byte)change;
                dr["Change"] = getChangeString(change);
                dtChanges.Rows.Add(dr);
            }
        }
        protected void createTempFields(DataTable sourceTable)
        {
            tmpFields = sourceTable.Clone();
            DataColumn col = new DataColumn("CompareIt", System.Type.GetType("System.Byte"));
            tmpFields.Columns.Add(col);
        }
        protected string getFilterFor(string tableName, string indexName, bool doIndex)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append((doIndex ? SqlQueries.Field_TableName : SqlQueries.Field_Name));
            sb.Append("='");
            sb.Append(tableName);
            sb.Append("'");
            if (!String.IsNullOrEmpty(indexName))
            {
                sb.Append(" AND ");
                sb.Append(SqlQueries.Field_IndexName);
                sb.Append("='");
                sb.Append(indexName);
                sb.Append("'");
            }
            return sb.ToString();
        }
        protected void deleteFromTemp(string tableName, string indexName)
        {
            bool doIndex = (dtTemp.Columns.IndexOf(SqlQueries.Field_IndexName) >= 0);
            DataRow[] dr = dtTemp.Select(getFilterFor(tableName, indexName, doIndex));
            for (int i = 0; i < dr.Length; ++i)
                dtTemp.Rows.Remove(dr[i]);
        }
        protected bool checkExclusions(string nameToCheck)
        {
            if (exclusions != null)
            {
                string findIt = exclusions.Where(x => nameToCheck.StartsWith(x)).FirstOrDefault();
                return !String.IsNullOrEmpty(findIt);
            }
            else
                return false;
        }
        protected void addTempToChanges()
        {
            bool doIndex = (dtTemp.Columns.IndexOf(SqlQueries.Field_IndexName) >= 0);
            // Add Server left overs
            if (dtTemp != null && dtTemp.Rows.Count > 0)
            {
                setMaxPosition(dtTemp.Rows.Count - 1);
                setCurrentPosition(0);
                for (int i = 0; i < dtTemp.Rows.Count; ++i)
                {
                    if (doIndex)
                    {
                        if (!checkExclusions(dtTemp.Rows[i][SqlQueries.Field_TableName].ToString()))
                            addToChanges(dtTemp.Rows[i][SqlQueries.Field_TableName] + " -> " + dtTemp.Rows[i][SqlQueries.Field_IndexName], ChangeType.NotExistInFile);
                    }
                    else
                    {
                        if (!checkExclusions(dtTemp.Rows[i][SqlQueries.Field_Name].ToString()))
                            addToChanges(dtTemp.Rows[i][SqlQueries.Field_Name].ToString(), ChangeType.NotExistInFile);
                    }
                    setCurrentPosition(i);
                }
            }
        }
        /// <summary>
        /// Loop file data and compare it to the server.
        /// </summary>
        /// <param name="nameType">Type to compare - Tables Or Store procs</param>
        /// <param name="compareOne">Delegate to be used to compare the item</param>
        protected void compareAll(string nameType, compareItem compareOne)
        {
            dtChanges.Rows.Clear();
            DataRow[] fileData = SqlMdFileData.Instance.FileNamesForType(nameType);
            dtTemp = DbHelper.Load_Table(SqlQueries.GetSqlMdSystemObjects(nameType));
            if (fileData != null && fileData.Length > 0)
            {
                setMaxPosition(fileData.Length - 1);
                setCurrentPosition(0);
                for (int i = 0; i < fileData.Length; ++i)
                {
                    if (!checkExclusions(fileData[i][SqlQueries.Field_Name].ToString()))
                        compareOne(nameType, fileData[i][SqlQueries.Field_Name].ToString());
                    //
                    deleteFromTemp(fileData[i][SqlQueries.Field_Name].ToString(), "");
                    //
                    setCurrentPosition(i);
                }
                // Add Server left overs
                addTempToChanges();
            }
        }
        #endregion

        #region - Public properties / variables -
        public DataTable FieldDiffs
        {
            get { return tmpFields; }
        }
        public DataTable Changes
        {
            get { return dtChanges; }
        }
        public String[] Exclusions
        {
            get { return exclusions; }
        }
        #endregion

        #region - Public methods - 
        public void SaveExludeList(string [] list)
        {
            if (SqlMdFileData.Instance.SaveExludeList(currentDb, list))
                exclusions = SqlMdFileData.Instance.GetExcludeList(currentDb);
        }
        #endregion
    }
}
